/* ---------------------------------------Marlin Toolkit----------------------------------------------*
Authors : Daniel Randle, Alex Bourne
Date Created : Mar 25 2013

File:
	inputmapping.cpp

Description:
	This file contains the definition for the InputMapping class functions along with any
	helper functions associated with the class
*------------------------------------------------------------------------------------------------------*/

#include <inputmapping.h>
#include <nscamera.h>
#include <nsobject.h>
#include <nsobjectreference.h>
#include <nsshader.h>
#include <nsobjectreferenceset.h>
#include <nsobjectset.h>
#include <nsshaderset.h>
#include <nsengine.h>
#include <nsframebufferobject.h>
#include <sstream>
#include <qcursor.h>
#include <mapwindow.h>
#include <logfile.h>
#include <math.h>
#include <debug.h>


InputMapping::InputMapping(NSEngine * eng, ToolkitGui::MapWindow * mapWindow):
	middleMouseHeld(false),
	rightMouseHeld(false),
	leftMouseHeld(false),
	angleMouseHeld(false),
	movementMouseHeld(false),
	objectZHeld(false),
	objectRotateXHeld(false),
	objectRotateYHeld(false),
	objectRotateZHeld(false),
	snapToGrid(true),
	selectionMode(false),
	addToSelection(false),
	paintingSelection(false),
	mouseMovedWithButton(false),
	invertY(Inverted),
	invertX(Inverted),
	engine(eng),
	mapWin(mapWindow),
	mousePos(NSVec2Df())
{
	#ifdef DEBUG
	d = NULL;
	#endif

	// Set up default keys
	keyMap.resize(ToggleRotate+1);
	keyMap[AngleMouse] = Qt::Key_Alt;
	keyMap[MovementMouse] = Qt::Key_Space;
	keyMap[ZoomIn] = Qt::Key_Equal;
	keyMap[ZoomOut] = Qt::Key_Minus;
	keyMap[ObjectMouseZ] = Qt::Key_Z;
	keyMap[ObjectRotateMouseX] = Qt::Key_X;
	keyMap[ObjectRotateMouseY] = Qt::Key_C;
	keyMap[ObjectRotateMouseZ] = Qt::Key_V;
	keyMap[ObjectMoveXPlus] = Qt::Key_Right;
	keyMap[ObjectMoveXMinus] = Qt::Key_Left;
	keyMap[ObjectMoveYPLus] = Qt::Key_Up;
	keyMap[ObjectMoveYMinus] = Qt::Key_Down;
	keyMap[ObjectMoveZPlus] = Qt::Key_Period;
	keyMap[ObjectMoveZMinus] = Qt::Key_Comma;
	keyMap[ObjectRotateXPlus] = Qt::Key_1;
	keyMap[ObjectRotateXMinus] = Qt::Key_2;
	keyMap[ObjectRotateYPlus] = Qt::Key_3;
	keyMap[ObjectRotateYMinus] = Qt::Key_4;
	keyMap[ObjectRotateZPlus] = Qt::Key_5;
	keyMap[ObjectRotateZMinus] = Qt::Key_6;
	keyMap[ObjectSnapToGrid] = Qt::Key_G;
	keyMap[CamInvertX] = Qt::Key_O;
	keyMap[CamInvertY] = Qt::Key_P;
	keyMap[CamForward] = Qt::Key_W;
	keyMap[CamForward] = Qt::Key_W;
	keyMap[CamReverse] = Qt::Key_S;
	keyMap[CamStrafeLeft] = Qt::Key_A;
	keyMap[CamStrafeRight] =Qt::Key_D;
	keyMap[CamElevate] = Qt::Key_E;
	keyMap[CamDrop] = Qt::Key_Q;
	keyMap[Delete] = Qt::Key_Delete;
	keyMap[BuildMode] = Qt::Key_L;
	keyMap[MultSelActivator] = Qt::Key_Control;
	keyMap[PaintSelection] = Qt::Key_Shift;
	keyMap[ToggleView] = Qt::Key_T;
	keyMap[ToggleCamera] = Qt::Key_F;
	keyMap[ToggleRotate] = Qt::Key_R;
}

void InputMapping::onMouseMovement(QMouseEvent * mEvent)
{

	NSVec2Df size( engine->width() ,engine->height() );
	NSVec2Df cMousePos(float(mEvent->pos().x()),float(mEvent->pos().y()));
	NSCamera * cam = engine->getCamera();
	NSSelectionModel * sel = engine->getSelectionModel();
	NSMapBuilder * mb = engine->getMapBuilder();

	
	if ( (leftMouseHeld && !sel->isSelectionBoxActive()) || mb->isActivated())
	{
		if (checkWrappedMouse(cMousePos))
			return;
	}

	NSVec2Df ds(cMousePos - mousePos);

	if (mb->isActivated())
	{
		if (objectZHeld)
			mb->moveZFromMouse(cMousePos,mousePos);
		else
			mb->moveXYFromMouse(cMousePos,mousePos);
	}

	if (paintingSelection && !sel->isSelectionBoxActive() && !mb->isActivated())
		selAtCurrentMousePos();

	if ( leftMouseHeld && !addToSelection && !rightMouseHeld && !middleMouseHeld && !mb->isActivated())
	{
		mouseMovedWithButton = true;
		if (!sel->isSelectionBoxActive())
		{
			if (objectZHeld)
				sel->moveZFromMouse(cMousePos,mousePos);
			else
				sel->moveXYFromMouse(cMousePos,mousePos);
		}
		else
		{
			if (cMousePos.x >= size.x-2)
				cMousePos.x = size.x-2;
			if (cMousePos.x <= 2)
				cMousePos.x = 2;
			if (cMousePos.y >= size.y-2)
				cMousePos.y = size.y-2;
			if (cMousePos.y <= 2)
				cMousePos.y = 2;
			sel->updateSelBox(cMousePos);
		}
	}

	if (middleMouseHeld || angleMouseHeld && !leftMouseHeld)
		cam->turn(ds.x*invertX,ds.y*invertY);

	if (rightMouseHeld || movementMouseHeld)
		cam->move(ds.x*invertX,ds.y*invertY);

	mousePos.x = float(mEvent->pos().x());
	mousePos.y = float(mEvent->pos().y());
}

void InputMapping::onMousePress(QMouseEvent * mEvent)
{
	mousePos.x = mEvent->pos().x();
	mousePos.y = mEvent->pos().y();
	NSSelectionModel * sel = engine->getSelectionModel();
	NSMapBuilder * mb = engine->getMapBuilder();

	if (mEvent->button() == Qt::LeftButton && !rightMouseHeld && !middleMouseHeld)
	{
		leftMouseHeld = true;
		if (mb->isActivated())
		{
			mb->addToMap();
			mapWin->postUpdate();
		}
		else
		{
			selAtCurrentMousePos();
			if ( !sel->isEmpty() )
			{
				mapWin->setCursor(QCursor( Qt::BlankCursor ));
				setMouseOffset();
			}
			else
			{
				sel->setSelectionBoxActive(true,mousePos);
				paintingSelection = true;
			}
		}
	}

	if (mEvent->button() == Qt::RightButton)
		rightMouseHeld = true;
	if (mEvent->button() == Qt::MiddleButton)
		middleMouseHeld = true;
}

void InputMapping::onMouseRelease(QMouseEvent * mEvent)
{
	NSSelectionModel * sel = engine->getSelectionModel();
	NSMapBuilder * mb = engine->getMapBuilder();
	if (leftMouseHeld && !sel->isEmpty() && !mb->isActivated())
	{
		if (!middleMouseHeld && !rightMouseHeld)
		{
			sel->snapAllToGrid();
			setMouseSelObjectCenter();
			if (mouseMovedWithButton)
				mouseMovedWithButton = false;
			else if (!addToSelection)
			{
				sel->clear();
				selAtCurrentMousePos();
			}
			mapWin->setCursor(QCursor( Qt::ArrowCursor ));
		}
	}

	if (mEvent->button() == Qt::LeftButton)
		leftMouseHeld = false;
	if (mEvent->button() == Qt::RightButton)
		rightMouseHeld = false;
	if (mEvent->button() == Qt::MiddleButton)
		middleMouseHeld = false;
	if (sel->isSelectionBoxActive())
	{
		NSVec2Df startPos = sel->getSelBoxStartPos();
		NSVec2Df tempMousePos = mousePos;

		if (mousePos.x < startPos.x)
		{
			if (mousePos.y < startPos.y)
			{
				tempMousePos = startPos;
				startPos = mousePos;
			}
			else
			{
				tempMousePos.x = startPos.x;
				startPos.x = mousePos.x;
			}
		}
		else
		{
			if (mousePos.y < startPos.y)
			{
				tempMousePos.y = startPos.y;
				startPos.y = mousePos.y;
			}
		}

		for (int x = tempMousePos.x; x >= startPos.x; x -= 10)
		{
			for (int y = tempMousePos.y; y >= startPos.y; y -= 10)
				selAtPos(NSVec2Df(x,y));
		}
		sel->setSelectionBoxActive(false);
		paintingSelection = false;
	}

}

void InputMapping::setMouseOffset()
{
	// Get the center of the selection model (center of first item selected)	
	NSVec3Df pos = engine->getSelectionModel()->getCenter();
	NSVec4Df pos2(pos.x,pos.y,pos.z,1.0f); // Create 4D vector from center
	pos2 = engine->getProjectionMatrix() * engine->getCameraMatrix() * pos2; // translate the center to screen space
	pos2 /= pos2.w;              // perspective divide because the pipeline is not doing it

	// Set the offset of each mouse coord so that when repositioning mouse after object moves
	// it will appear the same distance from the selection center as when first moved selection
	// The first part of both equations gets a normalized value in to DC
	offset.x = ((pos2.x+1.0f)/2.0f) * float( engine->width() ) - mousePos.x;
	offset.y = ((-pos2.y+1.0f)/2.0f) * float( engine->height() ) - mousePos.y;
}

void InputMapping::setMouseAtObject(const NSVec3Df & objPos)
{
	NSVec4Df pos2(objPos.x,objPos.y,objPos.z,1.0f);
	pos2 = engine->getProjectionMatrix() * engine->getCameraMatrix() * pos2; // Transform center to screen coords
	pos2 /= pos2.w; // and perspective divide

	// Here we set the new mouse position relative to the offsets determined by setMouseOffset so that the mouse
	// will pop up in the same position relative to the selection center as when the move operation began
	// Again we must take the normalized positions and convert them to DC
	mousePos.x = ((pos2.x+1.0f)/2.0f) * float( engine->width() ) - offset.x;
	mousePos.y = ((-pos2.y+1.0f)/2.0f) * float( engine->height() ) - offset.y;
	// Qt Map the cursor from the mapWindow screen space to the applications screen space
	QCursor::setPos( mapWin->mapToGlobal( QPoint(mousePos.x,mousePos.y) ) );
}

void InputMapping::setMouseSelObjectCenter()
{
	NSSelectionModel * sel = engine->getSelectionModel();
	if (!sel->isEmpty())
	{
		// Here we get the center of the selection model
		setMouseAtObject(sel->getCenter());
	}
}

bool InputMapping::onKeyPress(QKeyEvent * kEvent)
{
	int key = kEvent->key();
	NSCamera * cam = engine->getCamera();

	if (key == keyMap[AngleMouse])
	{
		angleMouseHeld = true;
		return true;
	}
	else if (key == keyMap[MovementMouse])
	{
		movementMouseHeld = true;
		return true;
	}
	else if (key == keyMap[ZoomIn])
	{
		cam->zoom(NSCamera::ZoomIn);
		return true;
	}
	else if (key == keyMap[ZoomOut])
	{
		cam->zoom(NSCamera::ZoomOut);
		return true;
	}
	else if (key == keyMap[ObjectMouseZ])
	{
		objectZHeld = true;
		return true;
	}
	else if (key == keyMap[ObjectRotateMouseX])
	{
		objectRotateXHeld = true;
		return true;
	}
	else if (key == keyMap[ObjectRotateMouseY])
	{
		objectRotateYHeld = true;
		return true;
	}
	else if (key == keyMap[ObjectRotateMouseZ])
	{
		objectRotateZHeld = true;
		return true;
	}
	else if (key == keyMap[ObjectMoveXPlus])
	{
		moveObject(true,false,false,1);
		return true;
	}
	else if (key == keyMap[ObjectMoveXMinus])
	{
		moveObject(true,false,false,-1);
		return true;
	}
	else if (key == keyMap[ObjectMoveYPLus])
	{
		moveObject(false,true,false,1);
		return true;
	}
	else if (key == keyMap[ObjectMoveYMinus])
	{
		moveObject(false,true,false,-1);
		return true;
	}
	else if (key == keyMap[ObjectMoveZPlus])
	{
		moveObject(false,false,true,1);
		return true;
	}
	else if (key == keyMap[ObjectMoveZMinus])
	{
		moveObject(false,false,true,-1);
		return true;
	}
	else if (key == keyMap[ObjectRotateXPlus])
	{
		rotateObject(true,false,false,1);
		return true;
	}
	else if (key == keyMap[ObjectRotateXMinus])
	{
		rotateObject(true,false,false,-1);
		return true;
	}
	else if (key == keyMap[ObjectRotateYPlus])
	{
		rotateObject(false,true,false,1);
		return true;
	}
	else if (key == keyMap[ObjectRotateYMinus])
	{
		rotateObject(false,true,false,-1);
		return true;
	}
	else if (key == keyMap[ObjectRotateZPlus])
	{
		rotateObject(false,false,true,1);
		return true;
	}
	else if (key == keyMap[ObjectRotateZMinus])
	{
		rotateObject(false,false,true,-1);
		return true;
	}
	else if (key == keyMap[ObjectSnapToGrid])
	{
		NSSelectionModel * sel = engine->getSelectionModel();
		sel->toggleSelectedGridSnap();
		return true;
	}
	else if (key == keyMap[CamInvertX])
	{
		if (invertX == NotInverted)
			invertX = Inverted;
		else
			invertX = NotInverted;
		return true;
	}
	else if (key == keyMap[CamInvertY])
	{
		if (invertY == NotInverted)
			invertY = Inverted;
		else
			invertY = NotInverted;
		return true;
	}
	else if (key == keyMap[CamForward])
	{
		cam->fly(NSCamera::FRONT);
		return true;
	}
	else if (key == keyMap[CamReverse])
	{
		cam->fly(NSCamera::BACK);
		return true;
	}
	else if (key == keyMap[CamStrafeLeft])
	{
		cam->strafe(NSCamera::LEFT);
		return true;
	}
	else if (key == keyMap[CamStrafeRight])
	{
		cam->strafe(NSCamera::RIGHT);
		return true;
	}
	else if (key == keyMap[CamElevate])
	{
		cam->elevate(NSCamera::UP);
		return true;
	}
	else if (key == keyMap[CamDrop])
	{
		cam->elevate(NSCamera::DOWN);
		return true;
	}
	if (key == keyMap[Delete])
	{
		deleteSelectedRefs();
		return true;
	}
	else if (key == keyMap[BuildMode])
	{
		NSMapBuilder * mb = engine->getMapBuilder();
		enterBuildMode(!mb->isActivated());
		return true;
	}
	else if (key == keyMap[MultSelActivator])
	{
		addToSelection = true;
		return true;
	}
	else if (key == keyMap[PaintSelection])
	{
		paintingSelection = true;
		return true;
	}
	else if (key == keyMap[ToggleView])
	{
		cam->toggleView();
		return true;
	}
	else if (key == keyMap[ToggleCamera])
	{
		cam->toggleCamera();
		return true;
	}
	else if (key == keyMap[ToggleRotate])
	{
		cam->quickRotate();
		return true;
	}
	else
		return false;
}

bool InputMapping::onKeyRelease(QKeyEvent * kEvent)
{
	int key = kEvent->key();
	NSCamera * cam = engine->getCamera();

	if (key == keyMap[AngleMouse])
	{
		angleMouseHeld = false;
		return true;
	}
	else if (key == keyMap[MovementMouse])
	{
		movementMouseHeld = false;
		return true;
	}
	else if (key == keyMap[ObjectMouseZ])
	{
		objectZHeld = false;
		return true;
	}
	else if (key == keyMap[ObjectRotateMouseX])
	{
		objectRotateXHeld = false;
		return true;
	}
	else if (key == keyMap[ObjectRotateMouseY])
	{
		objectRotateYHeld = false;
		return true;
	}
	else if (key == keyMap[ObjectRotateMouseZ])
	{
		objectRotateZHeld = false;
		return true;
	}
	else if (key == keyMap[CamForward])
	{
		cam->stopFlying();
		return true;
	}
	else if (key == keyMap[CamReverse])
	{
		cam->stopFlying();
		return true;
	}
	else if (key == keyMap[CamStrafeLeft])
	{
		cam->stopStrafing();
		return true;
	}
	else if (key == keyMap[CamStrafeRight])
	{
		cam->stopStrafing();
		return true;
	}
	else if (key == keyMap[CamElevate])
	{
		cam->stopElevating();
		return true;
	}
	else if (key == keyMap[CamDrop])
	{
		cam->stopElevating();
		return true;
	}
	else if (key == keyMap[MultSelActivator])
	{
		addToSelection = false;
		return true;
	}
	else if (key == keyMap[PaintSelection])
	{
		paintingSelection = false;
		return true;
	}
	else
		return false;
}

void InputMapping::onMouseWheel(QWheelEvent * wEvent)
{
	if (wEvent->delta() > 0)
		engine->getCamera()->zoom(NSCamera::ZoomIn);
	else
		engine->getCamera()->zoom(NSCamera::ZoomOut);
}

void InputMapping::setFunctionKey(Qt::Key key, Function func)
{
	keyMap[func] = key;
}

bool InputMapping::checkWrappedMouse(NSVec2Df & pos)
{
	bool ret = false;
	NSVec2Df size(engine->width(),engine->height());

	if (pos.x >= size.x - 1.0f)
	{
		mousePos.x = 2.0f; 
		pos.x = mousePos.x;
		QCursor::setPos( mapWin->mapToGlobal( QPoint(mousePos.x,pos.y) ) );
		ret = true;
	}
	if (pos.x <= 1.0f)
	{
		mousePos.x = size.x - 2.0f;
		pos.x = mousePos.x;
		QCursor::setPos( mapWin->mapToGlobal( QPoint(mousePos.x,pos.y) ) );
		ret = true;
	}
	if (pos.y >= size.y - 1.0f)
	{
		mousePos.y = 2.0f;
		pos.y = mousePos.y;
		QCursor::setPos( mapWin->mapToGlobal( QPoint(pos.x,mousePos.y) ) );
		ret = true;
	}
	if (pos.y <= 1.0f)
	{
		mousePos.y = size.y - 2.0f;
		pos.y = mousePos.y;
		QCursor::setPos( mapWin->mapToGlobal( QPoint(pos.x,mousePos.y) ) );
		ret = true;
	}
	return ret;
}

void InputMapping::moveObject(bool xDir, bool yDir, bool zDir, int direction)
{
}

void InputMapping::postGUIUpdate()
{
	
}

void InputMapping::rotateObject(bool xAxis, bool yAxis, bool zAxis, int direction)
{
	NSSelectionModel * sel = engine->getSelectionModel();
	if (xAxis)
		sel->rotate(30.0f*direction,0,0);
	if (yAxis)
		sel->rotate(0,30.0f*direction,0);
	if (zAxis)
		sel->rotate(0,0,30.0f*direction);
}

void InputMapping::selAtCurrentMousePos()
{
	selAtPos(mousePos);
}

void InputMapping::selAtPos(const NSVec2Df & mPosition)
{
	NSEngine::ObjectIndex obInd = engine->getObjectIndex(mPosition);
	NSSelectionModel * sel = engine->getSelectionModel();
	NSMapBuilder * mb = engine->getMapBuilder();
	NSObject * selObject = NULL;
	// First we make sure that the object index is not larger than the object count - which it should never be.
	// If the object index is picked from a blank screen it will return a wrapped around value which is huge
	selObject = engine->getObject(obInd.objectIndex);
	if (selObject != NULL)
	{

		// Now check to make sure the reference index picked is not larger than the instance count of the object
		// The reference ID should never be larger than the instance count - the id is the index in to the instance
		// transform vector contained by the base object
		if (obInd.referenceIndex < selObject->getInstanceCount())
		{
			// Pick the reference with the base object : selObject and with the ID referenceIndex and
			// add it to the selection

			NSObjectReference * selReference = engine->getReference(obInd.referenceIndex, selObject);
			if (addToSelection || paintingSelection)
			{
				if (sel->isIn(selReference) && !paintingSelection )
					sel->remove(selReference);
				else
					sel->add(selReference);
			}
			else if (!sel->isIn(selReference))
			{
				sel->clear();
				sel->add(selReference);
			}
			else
				sel->add(selReference);

			engine->getCamera()->setRotationPoint(sel->getCenter()*-1);
		}
	}
	else
	{
		// If an object wasn't picked that means the user clicked somewhere else on the screen - if the addToSelection
		// key is held then save the selection - otherwise deselect everything
		if (addToSelection || paintingSelection)
		{
			return; // do nothing
		}
		deselect(); // deselect everything
	}
	mb->setObject(selObject);
}

void InputMapping::deselect()
{

	NSSelectionModel * sel = engine->getSelectionModel();
	sel->clear();
	engine->getCamera()->setRotationPoint(NSVec3Df());
}

void InputMapping::setSelection(NSObjectReference* item)
{
	NSSelectionModel * sel = engine->getSelectionModel();
	sel->add(item);
	engine->getCamera()->setRotationPoint(sel->getCenter()*-1);
}

void InputMapping::deleteSelectedRefs()
{
	engine->getSelectionModel()->del();
	mapWin->postUpdate();
}

void InputMapping::setEngine(NSEngine * eng)
{
	engine = eng;
}

void InputMapping::enterBuildMode(bool enter)
{
	NSMapBuilder * mb = engine->getMapBuilder();
	NSSelectionModel * sel = engine->getSelectionModel();
	NSVec3Df pos = engine->getScreenCenterWolrdPos();
	//if (!sel->isEmpty() && engine->isOnScreen(sel->getCenter()))
	//{
	//	pos = sel->getCenter();
	//	if (sel->getCenterObjectType() == NSObject::HexTile)
	//		pos.z -= 0.45f; // hard coded will be changed
	//}

	if (enter)
	{
		if (mb->activate(pos,NSVec3Df(),NSVec3Df(0.8f,0.8f,0.8f)))
		{
			sel->clear();
			mapWin->setCursor(QCursor( Qt::BlankCursor ));
			mapWin->grabMouse();
		}
	}
	else
	{
		setMouseAtObject(mb->getPosition());
		if (mb->deactivate())
		{
			mapWin->setCursor(QCursor( Qt::ArrowCursor ));
			mapWin->releaseMouse();
		}
	}
}

#ifdef DEBUG
void InputMapping::setDebug(Debug * dbg)
{
	d = dbg;
}
#endif